package oojo.data.impl;

import java.util.Set;
import java.util.Map;
import java.util.HashMap;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import oojo.data.Query;
import oojo.data.Repository;
import oojo.data.RepositoryException;

public class RepositoryImpl<E> implements Repository<E> {

    private static final Logger logger = LoggerFactory.getLogger(RepositoryImpl.class);
    
    private Class<E> entityClass;
    private EntityManager entityManager;
    private Map<Class<?>, BaseFilter> filters;
    private Map<Class<?>, BaseQuery<E, Object>> queries;
    
    public Class<E> getEntityClass() {
        return entityClass;
    }
    
    public void setEntityClass(Class<E> entityClass) {
        this.entityClass = entityClass;
    }
    
    public EntityManager getEntityManager() {
        return entityManager;
    }
    
    @PersistenceContext
    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }
    
    public Map<Class<?>, BaseFilter> getFilters() {
        if (filters == null) {
            filters = new HashMap<Class<?>, BaseFilter>();
        }
        
        return filters;
    }
    
    public Map<Class<?>, BaseQuery<E, Object>> getQueries() {
        if (queries == null) {
            queries = new HashMap<Class<?>, BaseQuery<E, Object>>();
        }
        
        return queries;
    }
    
    public void setFilterTypes(Set<Class<?>> types) {
        logger.info("Initializing filters for: " + this);
        
        try {
            for (Class<?> klass : types) {
                logger.info("Initializing filter: " + klass.getName());
                
                BaseFilter f = (BaseFilter) klass.newInstance();
                getFilters().put(f.getFilterClass(), f);
            }
        } catch (Exception ex) {
            throw new RepositoryException(ex);
        }
    }
    
    @SuppressWarnings("unchecked")
    public void setQueryTypes(Set<Class<?>> types) {
        logger.info("Initializing queries for: " + this);
        
        try {
            for (Class<?> klass : types) {
                logger.info("Initializing query: " + klass.getName());
                
                BaseQuery<E, Object> q = (BaseQuery<E, Object>) klass.newInstance();
                q.setRepository(this);
                getQueries().put(q.getQueryClass(), q);
            }
        } catch (Exception ex) {
            throw new RepositoryException(ex);
        }
    }
    
    @SuppressWarnings("unchecked")
    public <R> R execute(Query<E, R> spec) {
        logger.debug("---> execute(spec)");
        
        BaseQuery<E, R> query = (BaseQuery<E, R>) getQueries().get(spec.getClass());
        
        if (query == null) {
            throw new RepositoryException("No query match for specification: " + spec.getClass().getName());
        }
        
        return query.execute(spec);
    }
}
